/* =============================================================
 * input-select.js v1.0.0
 * =============================================================
 * Original written by Ysy
 * =============================================================
 * Date 01/08/2019
 * ============================================================ */


(function (factory) {
    "use strict";
    if (typeof define === 'function' && define.amd) {
        define(['jquery'], factory);
    } else {
        factory(jQuery);
    }
}(function ($) {

    let InputSelect = function (element, options) {
        this.options = $.extend({}, $.fn.inputSelect.defaults, options);
        this.$element = $(element);
        this.$main = $(this.options.main);
        this.$menu = $(this.options.menu);
        this.source = this.options.source;
        this.afterSelect = this.options.afterSelect;
        this.afterDisplayText = this.options.afterDisplayText;
        this.shown = this.options.shown;
        this.focus = this.options.focus;
        this.mouseEnter = this.options.mouseEnter;
        this.pageNum = 1;
        this.pageSize = 10;
        this.listen();
        this.input();
    };

    InputSelect.prototype = {

        constructor: InputSelect,

        render: function (data) {// 渲染
            if (data.length === 0) {
                this.hide();
                return;
            }

            let that = this;
            let items = $(data).map((index, item) => {

                index = $(that.options.item);
                that.displayText(index, item);

                return index[0];
            });
            $(items[0]).addClass("active");
            this.$menu.html(items);

            this.$main.append(this.$menu);
            this.$element.after(this.$main);

            this.show();
        },

        displayText: function (ele, data) {// 显示文本
            ele.data("id", data.id);
            ele.data("text", data.text);
            ele.html(data.text);
            this.afterDisplayText(ele, data);
        },

        show: function () {
            if (!this.shown) return;

            let width = this.options.width === "" ? this.$element[0].offsetWidth : this.options.width;
            let height = this.$element[0].offsetHeight;
            let left = this.$element.position().left;
            let top = this.$element.position().top + height - 5;
            this.$main.css({"width": width, "top": top, "left": left,});
            this.$main.fadeIn(300);
        },

        hide: function () {
            this.shown = false;
            this.$main.fadeOut(0);
            this.destroy();
        },

        listen: function () {
            this.$element
                .on("input", $.proxy(this.input, this))
                // .on("click", $.proxy(this.input, this))
                .on("blur", $.proxy(this.blur, this))
                .on("keyup", $.proxy(this.keyup, this))
                .on("keypress", $.proxy(this.keypress, this));

            // this.$main
            //     .on("scroll", $.proxy(this.listenScroll, this));

            this.$menu.on("click", "li", $.proxy(this.click, this))
                .on("mouseenter", "li", $.proxy(this.mouseenter, this))
                .on("mouseleave", $.proxy(this.mouseleave, this))
        },

        listenScroll: function (e) {
            let aa = this.$main.scrollTop();
            let bb = this.$main.offset().top;
            this.pageNum++;
            if (aa > 300) {
                this.source($.proxy(this.process, this, this.pageNum));
            }
            if ($.isFunction(this.source)) {
                this.source($.proxy(this.process, this));
            } else if (this.source) {
                this.render(this.source);
            }
        },

        input: function () {
            this.shown = true;
            if ($.isFunction(this.source)) {
                this.source($.proxy(this.process, this));
            } else if (this.source) {
                this.render(this.source);
            }
        },

        blur: function () {
            if (this.mouseEntered) return;
            this.hide();
        },

        keyup: function (e) {
            // if (this.suppressKeyPressRepeat) return;
            this.move(e);
        },

        keypress: function (e) {
            if (!this.shown) return;

            switch (e.keyCode) {
                case 9: // tab
                case 13: // enter
                    e.preventDefault();
                    this.select();
                    this.hide();
                    break;
                case 27: // escape
                case 38: // up arrow
                case 40: // down arrow
            }
        },

        click: function (e) {
            e.preventDefault();
            this.select();
            this.hide();
        },

        mouseenter: function (e) {
            this.mouseEntered = true;
            this.$menu.find(".active").removeClass("active");
            $(e.currentTarget).addClass("active");
        },

        mouseleave: function () {
            this.mouseEntered = false;
        },

        select: function () {
            let value = this.$menu.find(".active");
            this.$element.val(value.data("text"));
            this.afterSelect(value.data("id"), value.data("text"), this.$element);
        },

        move: function (e) {
            if (!this.shown) return;

            switch (e.keyCode) {
                case 9: // tab
                case 13: // enter
                case 27: // escape
                    e.preventDefault();
                    break;

                case 38: // up arrow
                    // with the shiftKey (this is actually the left parenthesis)
                    if (e.shiftKey) return;
                    e.preventDefault();
                    this.prev();
                    break;

                case 40: // down arrow
                    // with the shiftKey (this is actually the right parenthesis)
                    if (e.shiftKey) return;
                    e.preventDefault();
                    this.next();
                    break;
            }
        },

        prev: function () {
            let active = this.$menu.find(".active").removeClass("active");
            active = active.prev();

            if (!active.length)
                active = this.$menu.find("li").last();

            this.scroll(active);
            active.addClass("active");
        },

        next: function () {
            let active = this.$menu.find(".active").removeClass("active");
            active = active.next();

            if (!active.length) {
                active = $(this.$menu.find("li").first());
            }

            this.scroll(active);
            active.addClass("active");
        },

        scroll: function (ele) {
            this.$main.animate({scrollTop: this.$main.scrollTop() + ele.offset().top - this.$main.offset().top}, 150);
        },

        process: function (data) {
            this.render(data);
        },

        destroy: function () {
            this.$element.parent().find("div." + this.$main.prop("class")).detach();
        },
    };

    let old = $.fn.inputSelect;

    $.fn.inputSelect = function (options) {
        return new InputSelect(this, options);
    };

    $.fn.inputSelect.defaults = {
        main: "<div class='input-select-ysy'></div>",
        menu: "<ul></ul>",
        item: "<li></li>",
        source: [],
        shown: false,
        focused: false,
        mouseEntered: false,
        width: "",
        afterSelect: $.noop,
        afterDisplayText: $.noop,
    }

    $.fn.inputSelect.Constructor = InputSelect;

    $.fn.inputSelect.noConflict = function () {
        $.fn.inputSelect = old;
        return this;
    };
}));
